package com.birdcage.goldminer.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Vector;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.birdcage.gamemanagers.GraphicsElement;

enum EUIComponetType
{
    eUICT_Root,
    eUICT_Button, 
    eUICT_Pane, 
    eUICT_Icon, 
    eUICT_Text, 
    eUICT_ProgressBar, 
    eUICT_Menu, 
    eUICT_Window
}

///This class is the base class of all UI element. It is working as 2D.
public class UIComponent
{
    // /private member
    private String m_name; // just use for representing
    private Vector2 m_relativePos; // Position off the parent
    private Vector2 m_worldPos;
    private Vector2 m_size;
    private EUIComponetType m_type;
    private UIComponent m_parent = null;
    private HashMap<String, Vector<UIComponent>> m_child = new HashMap<String, Vector<UIComponent>>(); // second
    private boolean m_visible = true; //if render
    private boolean m_active = true; //if answer mouse operation
    private ArrayList<GraphicsElement> m_renderUnits = new ArrayList<GraphicsElement>();
    
    // /Construct
    public UIComponent(String name, EUIComponetType type)
    {
        m_name = name;
        SetType(type);
    };

    // /Public method

    /*
     * Find a component with name. 
     * @name: The name of the component you're looking for. 
     * @recursion: If look up the name in the component's child components. 
     * @completeRecur: When recursion is true, if stop recursion when we find the first one match the name.
     */
    public Vector<UIComponent> GetChildByName(String name, boolean recursion, boolean completeRecur)
    {
        Vector<UIComponent> result = new Vector<UIComponent>();
        Vector<UIComponent> resultOfChild = m_child.get(name);
        if (resultOfChild != null)
        {
            result.addAll(resultOfChild);

            // if we don't need to complete recursion,
            // we will stop recursion after we found one result.
            if (recursion && !completeRecur)
            {
                recursion = false;
            }
        }
        if (recursion)
        {
            Iterator<Entry<String, Vector<UIComponent>>> iter = m_child.entrySet().iterator();
            boolean foundOne = false;
            while (iter.hasNext() && !foundOne)
            {
                Entry<String, Vector<UIComponent>> entry = iter.next();
                Vector<UIComponent> components = entry.getValue();
                for (int i = 0; i < components.size(); ++i)
                {
                    UIComponent comp = components.get(i);
                    Vector<UIComponent> childResult = comp.GetChildByName(name, recursion, completeRecur);
                    if (childResult.size() > 0)
                    {
                        result.addAll(childResult);
                        // if we don't need to complete recursion,
                        // we will stop recursion after we found one result.
                        if (!completeRecur)
                        {
                            foundOne = true;
                            break;
                        }
                    }
                }
            }
        }
        return result;
    }

    public Vector<UIComponent> GetChildByName(String name)
    {
        return GetChildByName(name, false, false);
    }

    public Vector<UIComponent> GetChildByName(String name, boolean recursion)
    {
        return GetChildByName(name, recursion, false);
    }

    // Add an UIComoponent to another as a child.
    // If it already has a parent, it will be removed from the Ex-parent.
    public void AddChild(UIComponent comp)
    {
        // Connect this as parent.
        comp.ConnectParent(this);

        // Add it to the child member
        String name = comp.GetName();
        Vector<UIComponent> child = m_child.get(name);
        if (child == null)
        {
            Vector<UIComponent> newChild = new Vector<UIComponent>();
            newChild.add(comp);
            m_child.put(name, newChild);
        } else
        {
            child.add(comp);
        }
    }

    // Remove a child, if the container is empty after it is removed, we will
    // also remove the map element.
    public void RemoveChild(UIComponent comp)
    {
        String name = comp.GetName();
        Vector<UIComponent> child = m_child.get(name);
        if (child.size() > 1)
        {
            child.remove(comp);
        } else
        {
            m_child.remove(name);
        }
    }

    // /Get child components
    public HashMap<String, Vector<UIComponent>> GetChild()
    {
        return m_child;
    }

    public String GetName()
    {
        return m_name;
    }

    public void SetRelativePos(Vector2 pos)
    {
        m_relativePos = pos;

        m_worldPos = (m_parent != null) ? m_relativePos.add(m_parent.GetWorldPos()) : m_relativePos;  

    }

    public Vector2 GetRelativePos()
    {
        return m_relativePos;
    }

    public Vector2 GetWorldPos()
    {
        return m_worldPos;
    }

    public void SetSize(Vector2 size)
    {
        m_size = size;
    }

    public Vector2 GetSize()
    {
        return m_size;
    }

    public void Render(SpriteBatch spriteBatch)
    {
        if (m_visible)
        {
            OnRender(spriteBatch);
            Iterator<Entry<String, Vector<UIComponent>>> iter = m_child.entrySet().iterator();
            while (iter.hasNext())
            {
                Entry<String, Vector<UIComponent>> entry = iter.next();
                Vector<UIComponent> components = entry.getValue();
                for (int i = 0; i < components.size(); ++i)
                {
                    UIComponent comp = components.get(i);
                    comp.Render(spriteBatch);
                }
            }
        }
    }
    
    //override this function.
    public void OnRender(SpriteBatch spriteBatch)
    {
        for (int i = 0; i < m_renderUnits.size(); ++i)
        {
            GraphicsElement elem = m_renderUnits.get(i);
            elem.Update(spriteBatch);
        }
    }

    public void SetType(EUIComponetType m_type)
    {
        this.m_type = m_type;
    }

    public EUIComponetType GetType()
    {
        return m_type;
    } 

    //This function should be override in special case (non-regular shape ect...).
    public boolean IsPointInside(float x, float y)
    {
        boolean ret = false;
        Vector2 rightUp = m_worldPos.add(m_size);
        if (x >= m_worldPos.x && y >= m_worldPos.y && x <= rightUp.x && y <= rightUp.y) 
        {
            ret = true;
        }
        return ret;
    }

    public boolean IsPointInside(Vector2 point)
    {
        return IsPointInside(point.x, point.y);
    }

    public boolean IsVisible() 
    {
        return m_visible;
    }

    public void setVisible(boolean visible) 
    {
        m_visible = visible;
    }

    public boolean HandleTouch(Vector2 touchPos)
    {
        boolean result = false;
        if (m_active && IsPointInside(touchPos.x, touchPos.y))
        {
            result = result || OnHandleTouch();            
            
            Iterator<Entry<String, Vector<UIComponent>>> iter = m_child.entrySet().iterator();
            while (iter.hasNext())
            {
                Entry<String, Vector<UIComponent>> entry = iter.next();
                Vector<UIComponent> components = entry.getValue();
                for (int i = 0; i < components.size(); ++i)
                {
                    UIComponent comp = components.get(i);
                    result = result || comp.HandleTouch(touchPos);
                }
            }
        }
        return result;
    }

    //Override this function.
    public boolean OnHandleTouch()
    {
        return false;
    }
    
    public void AddRenderUnit(GraphicsElement elem)
    {
        if (elem.IsUpdateByGraphicsManager())
        {
            m_renderUnits.add(elem);
        }
        else
        {
             assert(false);//"Trying to add a graphics element for game object!"
        }
    }

    // /Private method
    // UIComponent will leave its parent and become a top-parent component.
    private boolean DisconnectParent()
    {
        boolean success = false;
        if (m_parent != null)
        {
            m_parent.RemoveChild(this);
            m_parent = null;
            success = true;
        }
        return success;
    }

    // Connect a component as its parent, if it is already has a parent, it will
    // fail.
    private boolean ConnectParent(UIComponent parent)
    {
        boolean success = false;
        // remove the old parent first.
        DisconnectParent();

        if (m_parent == null)
        {
            m_parent = parent;
            success = true;
        }
        return success;
    }
}
